﻿namespace SEED.SEEDControllers {
    using System;
    using System.IO;

    public class SteimFrameBlock {

        private class SteimFrame {
            // 16 32-byte words
            public int[] word = new int[16];
            // word position in frame (pos: 0 = W0, 1 = W1, etc...)
            public int pos = 0;  
        }

        private int numFrames    = 0;           // number of frames this object contains
        private int numSamples   = 0;           // number of samples represented
        private int steimVersion = 0;           // Steim version number
        private int currentFrame = 0;           // number of current frame being built
        private SteimFrame[] steimFrame = null; // array of frames;

        public int NumFrames      { get { return numFrames; } }
        public int SteimVersion   { get { return steimVersion; } }
        public int NumSamples     { get { return numSamples;} }
        
        public byte[] EncodedData {
            get {
                MemoryStream memStream = new MemoryStream(numFrames * 64);
                BinaryWriter binWriter = new BinaryWriter(memStream);
                for (int i = 0; i < numFrames; i++) {
                    for (int j = 0; j < 16; j++) {
                        // write integer to byte stream
                        binWriter.Write(steimFrame[i].word[j]);
                    }
                }
                BinaryReader binReader = new BinaryReader(memStream);
                return binReader.ReadBytes((int)memStream.Length);
            }
        }

        public SteimFrameBlock(int numFrames, int steimVersion) {
            steimFrame = new SteimFrame[numFrames]; // array of frames
            for (int i = 0; i < steimFrame.Length; i++) {
                // initialize the SteimFrame array
                steimFrame[i] = new SteimFrame();
            }
            this.numFrames = numFrames;
            this.steimVersion = steimVersion;

            // initialize the first frame properly
            currentFrame = 0;                       // sanity
            AddEncodingNibble(0);                   // first nibble always 00
            this.steimFrame[currentFrame].pos++;    // increment position in frame to W1
        }

        private void AddEncodingNibble(int bitFlag) {
            // W0 nibble offset - determines Cn in W0
            int offset = steimFrame[currentFrame].pos;
            // how much to shift bitFlag
            int shift = (15 - offset) * 2;  
            steimFrame[currentFrame].word[0] |= (bitFlag << shift);
            return;
        }

        public void SetXn(int word) {
            steimFrame[0].word[2] = word;
        }

        public bool AddEncodedWord(int word, int samples, int nibble) {
            int pos = steimFrame[currentFrame].pos;     // word position
            steimFrame[currentFrame].word[pos] = word;  // add word
            AddEncodingNibble(nibble);                  // add nibble
            numSamples += samples;
            pos++;                                       // increment position in frame
            
            if (pos > 15) {                             // need next frame?
                currentFrame++;
                
                if (currentFrame >= numFrames) {        // exceeded frame limit?
                    return true;                        // block is full
                }
                AddEncodingNibble(0);                   // first nibble always 00
            }
            
            steimFrame[currentFrame].pos++;             // increment position in frame
            return false;                               // block is not yet full
        }
        
    }
}
